<?php
namespace App\SDK\PingAn\service;

use App\SDK\PingAn\library\Rsa;
use App\SDK\PingAn\library\Request;
use App\SDK\PingAn\library\Config;
use App\SDK\PingAn\service\Token;

class CommonRsa
{
    private $_rsa;
    private $_tokenService;

    public function request($data, $method)
    {
        $config = Config::get();
        $header = $this->getHeader($data);
        $url = $config['url']['base_common'] . $method;
        $response = Request::curlPost($url, json_encode($data), $header);
        if ($response) {
            $result = json_decode($response, true);
            if (!$this->isValid($result)) {
                $result = [
                    'Code' => 'E90092',
                    'Message' => '验签不通过'
                ];
            } else {
                if (isset($result['Code']) && $result['Code'] == 'E30001') {
                    $this->getTokenService()->removeCache();
                }
            }
        } else {
            $result = [
                'Code' => 'E90091',
                'Message' => '返回为空',
                'header' => $GLOBALS['G_HEADER'],
            ];
        }
        return $result;
    }

    public function invoke($data, $method) {
        $config = Config::get();
        $header = $this->getHeader($data);
        $url = $config['url']['base_common'] . $method;
        $response = Request::curlPost($url, json_encode($data), $header);
        if ($response) {
            $result = json_decode($response, true);
            if (!$this->isValid($result)) {
                $result = '验签不通过';
            } else {
                if (isset($result['Code']) && $result['Code'] == 'E30001') {
                    $this->getTokenService()->removeCache();
                }
            }
        }
        return [
            'code' => $GLOBALS['G_HTTP_CODE'],
            'header' => $GLOBALS['G_HEADER'],
            'body' => $result ?? ''
        ];
    }

    private function getHeader($data) {
        $data = [
            'x-pab-timestamp' =>  date('Y-m-d H:i:s'),
            'Accept' => 'text/plain',
            'x-pab-encrypt' => 'false',
            'x-pab-global-seqno' => time() . rand(1000, 9999),
            'User-Agent' => 'aop-sdk-php',
            'x-pab-signMethod' => 'RSA',
            'x-pab-appID' => Config::get('app_id'),
            'x-pab-signature' => $this->getSign($data),
            'Content-Type' => 'application/json',
            'Authorization' => $this->getToken(),
            'x-pab-version' => 'PHP-BOAP_1.0.0'
        ];
        return array_map(function ($key, $value){return $key . ': ' . $value;}, array_keys($data), $data);
    }

    private function getToken() {
        $token = '';
        $data = $this->getTokenService()->get();
        if (isset($data['Code']) && $data['Code'] == '000000') {
            $token = $data['content']['result']['access_token'];
        }
        return $token;
    }

    private function getTokenService() {
        if (is_null($this->_tokenService)) {
            $this->_tokenService = new Token;
        }
        return $this->_tokenService;
    }

    private function getSign($str) {
        if (is_array($str)) {
            $arr = []; $result = [];
            $this->assemble('', $str, $arr);
            ksort($arr);
            foreach ($arr as $key => $value) {
                if ($value !== '') {
                    $result[] = $key . '=' . $value;
                }
            }
            $str = implode('&', $result) . '&';
        }
        return $this->rsa()->sign($str);
    }

    private function isValid($data) {
        if (isset($GLOBALS['G_HEADER']['x-pab-signature']) && !empty($GLOBALS['G_HEADER']['x-pab-signature'])) {
            $arr = []; $result = [];
            $this->assemble('', $data, $arr);
            ksort($arr);
            foreach ($arr as $key => $value) {
                if ($value !== '') {
                    $result[] = $key . '=' . $value;
                }
            }
            $str = implode('&', $result) . '&';
            return $this->rsa()->isValid($str, $GLOBALS['G_HEADER']['x-pab-signature']);
        } else {
            return true;
        }
    }

    private function assemble($prev, $data, &$result) {
        foreach ($data as $key => $value) {
            if (is_array($value)) {
                $this->assemble($prev . (is_string($key) ? $key : ''), $value, $result);
            } else {
                $result[$prev . $key] = $value;
            }
        }
        return $result;
    }

    private function rsa() {
        if (is_null($this->_rsa)) {
            $this->_rsa = new Rsa();
        }
        return $this->_rsa;
    }
}
